<template>
  <!-- 用于触发文件分片上传对话框的按钮 -->
  <el-button type="primary" @click="handleUploadDialogShow">文件分片上传</el-button>
  <!-- 文件分片上传对话框 -->
  <el-dialog v-model="uploadDialogVisible" title="文件分片上传" width="900px">
    <el-upload
      class="upload-demo"
      :show-file-list="false"
      drag
      action="/"
      multiple
      :http-request="handleFileUploadRequest"
      :on-success="onSuccess"
    >
      <el-icon class="el-icon--upload">
        <upload-filled />
      </el-icon>
      <div class="el-upload__text"> 请拖拽文件到此处或 <em>点击此处上传</em> </div>
    </el-upload>

    <!-- 显示文件上传任务列表的表格 -->
    <el-table class="upload-table" :data="uploadTasks">
      <template #empty>数据为空</template>

      <el-table-column prop="taskName" label="名称" width="350px" align="center">
        <template #default="scope">
          <div class="task-name">
            {{ scope.row.taskName }}
          </div>
        </template>
      </el-table-column>

      <el-table-column label="大小" width="220px" align="center">
        <template #default="scope">
          <div class="file-container">
            <div class="file-size" style="color: #a5a6aa"
              >{{ formatBytes(scope.row.uploadedSize) }}/{{ scope.row.size }}
            </div>
            <div class="file-operation">
              <span
                @click="resumeUpload(scope.row)"
                v-show="scope.row.status === 3 || scope.row.status === 6"
              >
                <svg class="file-operation-icon">
                  <!-- xlink:href指定用哪一个图标,属性值必须是以"#icon-图标名字"的格式 -->
                  <!-- fill可以设置图标的颜色 -->
                  <use xlink:href="#icon-start"></use>
                </svg>
              </span>
              <span
                @click="pauseUpload(scope.row)"
                v-show="scope.row.status !== 3 && scope.row.status !== 6"
              >
                <svg class="file-operation-icon">
                  <use xlink:href="#icon-pause"></use>
                </svg>
              </span>
              <span @click="cancelUpload(scope.row)">
                <svg class="file-operation-icon" id="cancel">
                  <use xlink:href="#icon-cancel"></use>
                </svg>
              </span>
            </div>
          </div> </template
      ></el-table-column>
      <el-table-column label="状态" width="250px" align="center">
        <template #default="scope">
          <div>
            <div style="color: #a5a6aa; margin-bottom: 1px">{{
              getFileUploadStatusName(scope.row)
            }}</div>
            <el-progress :percentage="scope.row.progress" status="success" :show-text="false" />
          </div>
        </template>
      </el-table-column>
    </el-table>
  </el-dialog>

  <el-table :data="sysFiles" stripe style="width: 100%; height: 80vh">
    <el-table-column prop="name" label="文件名字" />
    <el-table-column prop="byteSize" label="文件大小" />
    <el-table-column prop="fileType" label="文件类型" />
    <el-table-column label="文件">
      <template #default="scope">
        <el-image
          v-if="canPreviewFile(scope.row)"
          style="width: 100px; height: 100px"
          :src="scope.row.url"
          :fit="fit"
        />
        <div v-else>不支持预览</div>
      </template>
    </el-table-column>
    <el-table-column label="操作">
      <template #default="scope">
        <el-button type="primary" @click="downloadFile(scope.row.id)">下载</el-button>
      </template>
    </el-table-column>
  </el-table>
</template>

<script setup>
import { UploadFilled } from '@element-plus/icons-vue'
import { merge, getSysFileList } from '../api/sysFileInfo/sysFileInfo'
import { ElMessageBox, ElNotification } from 'element-plus'

import { onMounted, reactive, ref } from 'vue'
import { getTaskInfo, Task } from '@/api/sysFileInfo/upload'
import { formatSpeed, formatBytes } from '@/utils/FileUtils'
import generationMd5 from '@/lib/generationMd5'
import { DEFAULT_SIZE } from '@/const/Const'

const sysFiles = ref([])
onMounted(() => {
  fetchFileList()
})

//控制上传文件弹窗
const uploadDialogVisible = ref(false)

/**
 * 文件分片上传按钮点击事件
 */
const handleUploadDialogShow = () => {
  uploadDialogVisible.value = true
}
/*
获取文件列表
 */
const fetchFileList = () => {
  getSysFileList().then((resp) => {
    sysFiles.value = resp.data
  })
}

/*
 上传成功后的回调
 */
const onSuccess = (__, uploadFile) => {
  let index = uploadTasks.findIndex((uploadTask) => uploadTask.uid === uploadFile.uid)
  //更改状态为已暂停
  if (index !== -1) {
    uploadTasks[index].status = 1
    uploadTasks[index].progress = 100
    setTimeout(() => {
      uploadTasks.splice(index, 1)
    }, 600)
  }

  fileTaskMap.delete(uploadFile.uid)
  uploadDialogVisible.value = false
  fetchFileList()
}

/*
判断文件是否支持预览
 */
const canPreviewFile = (sysFile) => {
  return ['PNG', 'JPEG', 'JPG', 'GIF'].indexOf(sysFile.fileType.toLocaleUpperCase()) !== -1
}

const fileTaskMap = new Map()

/**
 * 暂停上传
 */
const pauseUpload = (uploadTask) => {
  if (uploadTask.status === 0) {
    uploadTask.md5TaskResult.pause()
    //更改状态为已暂停
    uploadTask.status = 6
    return
  }

  if (uploadTask.status === 2) {
    let uid = uploadTask.uid
    let fileTask = fileTaskMap.get(uid)
    if (fileTask) {
      fileTask.pause()
    }

    //更改状态为已暂停
    uploadTask.status = 3
  }
}

/**
 * 恢复上传
 */
const resumeUpload = (uploadTask) => {
  if (uploadTask.status === 6) {
    uploadTask.md5TaskResult.resumeProcessing()
    uploadTask.status = 0
    return
  }

  if (uploadTask.status === 3) {
    let fileTask = fileTaskMap.get(uploadTask.uid)
    if (fileTask) {
      fileTask.resume()
    }

    //更改状态为处理中
    uploadTask.status = 2
  }
}

/**
 * 取消上传
 */
const cancelUpload = (uploadTask) => {
  ElMessageBox.alert('确定要取消此任务吗？', '取消任务', {
    // if you want to disable its autofocus
    // autofocus: false,
    confirmButtonText: '取消任务',
    showCancelButton: true,
    cancelButtonText: '继续保留',
    callback: (action) => {
      if (action === 'confirm') {
        uploadTask.md5TaskResult.cancel()

        let fileTask = fileTaskMap.get(uploadTask.uid)
        if (fileTask) {
          fileTask.cancel()
        }
        let index = uploadTasks.findIndex((item) => item.uid === uploadTask.uid)
        uploadTasks.splice(index, 1)
      }
    }
  })
}

/*
判断文件是否支持预览
 */
const getFileUploadStatusName = (uploadTask) => {
  switch (uploadTask.status) {
    case 0:
      return `文件正在校验中，请稍候 (${uploadTask.md5Process.toFixed(2)}%)`
    case 1:
      return '上传完成'
    case 2: //处理中
      let speed = formatSpeed(uploadTask.speed)
      if (uploadTask.remainingTime) {
        speed += ` - ${uploadTask.remainingTime}`
      }
      return speed
    case 3:
      return '已暂停'
    case 6:
      return '已暂停'
  }
}
/**
 *下载文件（需要跨域配置，否则就变成了预览文件）
 * @param id 文件id
 */
const downloadFile = (id) => {
  window.open(`/prod-api/v1/minio/tasks/download/${id}`)
}

// 文件上传分块任务的队列（用于移除文件时，停止该文件的上传队列） key：fileUid value： queue object
const fileUploadChunkQueue = ref({}).value

const uploadTasks = reactive([])

/**
 * el-upload 自定义上传方法入口
 */
const handleFileUploadRequest = async (options) => {
  // 获取上传的文件
  const file = options.file

  let uploadTask = reactive({
    taskName: file.name,
    size: formatBytes(file.size),
    uid: file.uid,
    remainingTime: null,
    speed: 0,
    uploadedSize: 0,
    md5Process: 0,
    status: 0,
    progress: 0,
    md5TaskResult: null
  })
  uploadTasks.push(uploadTask)
  const md5TaskResult = generationMd5(file, DEFAULT_SIZE, (currentChunk, totalChunk) => {
    uploadTask.md5Process = (currentChunk / totalChunk) * 100
  })
  uploadTask.md5TaskResult = md5TaskResult
  let md5 = await md5TaskResult.promise

  const task = await getTaskInfo(file, md5)
  uploadTask.status = 2
  if (!task) {
    ElNotification.error({
      title: '文件上传错误',
      message: '获取上传任务失败'
    })
    return
  }

  const { finished, path, taskRecord } = task

  // 如果文件上传任务已经完成，直接返回文件路径
  if (finished) {
    return path
  }

  const { md5Code } = taskRecord

  const fileTask = new Task(file, taskRecord, fileUploadChunkQueue)
  fileTask.processCallback = (speed, remainingTime, uploadedSize) => {
    uploadTask.speed = speed
    uploadTask.remainingTime = remainingTime
    uploadTask.uploadedSize = uploadedSize
    uploadTask.progress = fileTask.percent
  }

  fileTaskMap.set(file.uid, fileTask)
  // 处理文件上传分片，并获取上传失败的分片列表
  const errorList = await fileTask.handleUpload()

  // 如果有分片上传失败，显示错误通知并返回
  if (errorList.length > 0) {
    ElNotification.error({
      title: '文件上传错误',
      message: '部分分片上次失败，请尝试重新上传文件'
    })
    return
  }

  const { code, data, msg } = await merge(md5Code)
  if (code === 200000) {
    return path
  } else {
    ElNotification.error({
      title: '文件上传错误',
      message: msg
    })
  }
}
</script>

<style>
.upload-table {
  height: 300px;
  font-size: 13px;
  width: 900px;
}

.task-name {
  max-width: 350px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 默认状态下，隐藏文件操作 */
.file-operation {
  display: none;
}

.file-operation-icon {
  width: 14px;
  height: 14px;
  margin: 0 5px;
  fill: #a9aaad;
}

.file-operation-icon:hover {
  fill: #24252b;
}

#cancel:hover {
  fill: #ef5a50;
}

/* 鼠标悬浮时，隐藏文件大小并展示操作 */
.file-container:hover .file-size {
  display: none;
}

.file-container:hover .file-operation {
  display: block;
}
</style>
